package com.lwc.cfdns.aspect;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.lwc.cfdns.annotation.OperLog;
import com.lwc.cfdns.config.AppConfig;
import com.lwc.cfdns.pojo.entity.SystemAdmin;
import com.lwc.cfdns.pojo.entity.SystemOperLog;
import com.lwc.cfdns.service.impl.SystemAdminServiceImpl;
import com.lwc.cfdns.service.impl.SystemOperLogServiceImpl;
import com.lwc.cfdns.utils.ServletUtils;
import com.lwc.cfdns.utils.StringUtils;
import com.lwc.cfdns.utils.ip.IpUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 操作日志切面
 *
 * @author luwc
 * @title Log
 * @description
 * @time 2023/8/1 16:28:03
 * @version: 1.0
 */
@Aspect
@Order(20) // 定义切面执行顺序
@Component
public class OperLogAspect {
    private final Logger logger = LoggerFactory.getLogger(OperLogAspect.class);

    private final static ThreadLocal<Long> startTime = new ThreadLocal<>();

    @Autowired
    private SystemOperLogServiceImpl systemOperLogService;
    @Autowired
    private SystemAdminServiceImpl systemAdminService;

    /**
     * 定义切面
     * 自动记录admin下所有Controller
     * 或手动注解
     *
     * @param
     * @return void
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/8/1 16:33:20
     */
    @Pointcut("execution(public * com.lwc.cfdns.controller.admin.*.*.*(..)) || @annotation(com.lwc.cfdns.annotation.OperLog)")
    public void autoOperLog() {
        System.out.println("pointCut签名...");
    }

    /**
     * 环绕通知,环绕增强，相当于MethodInterceptor
     * 需要注意:@annotation(xxx) 时才可以跟第二个参数
     *
     * @param proceedingJoinPoint
     * @return Object
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/8/1 17:26:03
     */
    @Around("autoOperLog()")
    public Object aroundAnnotationAdvice(ProceedingJoinPoint proceedingJoinPoint) {
        // 记录开始执行时间
        startTime.set(System.currentTimeMillis());
        Object rtValue = null;
        String ipAddr = IpUtils.getIpAddr();
        HttpServletRequest request = ServletUtils.getRequest();
        String params = "";
        String result = "";
        String title = "自动匹配";
        String businessType = "other"; //
        String operatorType = "1"; // 操作类别（0其它 1后台用户 2手机端用户）
        String status = "0";
        long diffTime = 0l;
        String errorMsg = "";
        // 获取注解中的属性
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        OperLog operLog = signature.getMethod().getAnnotation(OperLog.class);
        // 类名
        String className = proceedingJoinPoint.getTarget().getClass().getName();
        // 方法名
        String methodName = proceedingJoinPoint.getSignature().getName();

        // 方法参数
        Object[] args = proceedingJoinPoint.getArgs();
        if (operLog == null) {
            Map<String, Object> module = new HashMap<>();
            // 自动事件
            title = proceedingJoinPoint.getTarget().getClass().getSimpleName();
            businessType = methodName;
        } else {
            // 注解手动
            title = operLog.title();
            businessType = operLog.businessType();
            operatorType = operLog.operatorType();
        }
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            params = objectMapper.writeValueAsString(args);

            // 前置事件 todo
//            logger.info("前置通知，类名:{},方法名:{},参数:{},注解属性:{}", className, methodName, args, title);

            // 重要，调用目标函数执行 反回为结果
            rtValue = proceedingJoinPoint.proceed();
            long endTime = System.currentTimeMillis();
            diffTime = endTime - startTime.get();

            // 后置事件 todo
//            logger.info("返回通知，类名:{},方法名:{},执行时间:{},参数:{},结果:{}", className, methodName, diffTime, args, rtValue);

            if (AppConfig.getOperLogResult()) {
                result = objectMapper.writeValueAsString(rtValue);
            }

            return rtValue;
        } catch (Throwable e) {
//            logger.info("异常通知");
            status = "1";
            errorMsg = e.getMessage();
            throw new RuntimeException(e.getMessage());
        } finally {
//            logger.info("后置通知,永远会执行,IP:{}", ipAddr);
            logger.info("操作日志,title:{},Url:{},requestMethod:{},controller:{},iP:{},businessType:{},operatorType:{},status:{},params:{},result:{},diffTime:{},errorMsg:{}", title, request.getRequestURI().toString(), request.getMethod(), className + "." + methodName, ipAddr, businessType, operatorType, status, params, result, diffTime, errorMsg);
            hanldlLog(
                    title, request.getRequestURI(), request.getMethod(),
                    className + "." + methodName + "()",
                    ipAddr, businessType, operatorType,
                    status, params,
                    result, diffTime, errorMsg);
        }

    }

    /**
     * 写入操作日志
     *
     * @param title
     * @param url
     * @param requestMethod
     * @param method
     * @param ipAddr
     * @param businessType
     * @param operatorType
     * @param status
     * @param params
     * @param result
     * @param costTime
     * @param errorMsg
     * @return void
     * @throws
     * @version 1.0.0
     * @author luwc
     * @time 2023/8/2 17:22:24
     */
    private void hanldlLog(String title, String url, String requestMethod, String method, String ipAddr, String businessType, String operatorType, String status, String params, String result, long costTime, String errorMsg) {
        SystemAdmin adminInfo = systemAdminService.getAdminInfo();
        SystemOperLog systemOperLog = new SystemOperLog();
        systemOperLog.setTitle(title);
        systemOperLog.setOperIp(ipAddr);
        systemOperLog.setOperName(adminInfo != null ? adminInfo.getUserName() : "");
        systemOperLog.setBusinessType(businessType);
        systemOperLog.setOperatorType(operatorType);
        systemOperLog.setStatus(status);//操作状态（0正常 1异常）
        systemOperLog.setOperParam(StringUtils.substring(params, 0, 2000));
        systemOperLog.setJsonResult(StringUtils.substring(result, 0, 2000));
        systemOperLog.setOperUrl(url);
        systemOperLog.setMethod(method);
        systemOperLog.setRequestMethod(requestMethod);
        systemOperLog.setOperTime(new Date());
        systemOperLog.setCostTime(costTime);
        systemOperLog.setErrorMsg(StringUtils.substring(errorMsg, 0, 2000));

        if (AppConfig.getAutoOperLog()) {
            // 调异步入库方法
            systemOperLogService.create(systemOperLog);
        }
    }
}
